/* Copyright  2009-2010
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename: lock.h
 */

#ifndef __LOCK_H__
#define __LOCK_H__

#include <os.h>
#include <index.h>

#include <definitions.h>

class Session;
class Table;
class TableList;

typedef struct collapsar_lock_st COLLAPSAR_LOCK                    ;

COLLAPSAR_LOCK *db_lock_tables (Session *session,
                                Table **table   ,
                                uint32_t count  ,
                                uint32_t flags  ,
                                bool *need_reopen);
/* dbsql_lock_tables() and open_table() flags bits */
#define COLLAPSAR_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
#define COLLAPSAR_LOCK_IGNORE_FLUSH                 0x0002
#define COLLAPSAR_LOCK_NOTIFY_IF_NEED_REOPEN        0x0004
#define COLLAPSAR_OPEN_TEMPORARY_ONLY               0x0008
#define COLLAPSAR_LOCK_PERF_SCHEMA                  0x0020

void db_unlock_tables     (Session *session, COLLAPSAR_LOCK *sql_lock);
void db_unlock_read_tables(Session *session, COLLAPSAR_LOCK *sql_lock);
void db_unlock_some_tables(Session *session, Table **table,uint32_t count);
void db_lock_remove       (Session *session, COLLAPSAR_LOCK *locked,
                           Table *table,
                           bool always_unlock);
void db_lock_abort     (Session *session, Table *table, bool upgrade_lock);
void db_lock_downgrade_write(Session *session, Table *table,
                                thr_lock_type new_lock_type);
bool db_lock_abort_for_thread(Session *session, Table *table);
COLLAPSAR_LOCK *db_lock_merge(COLLAPSAR_LOCK *a,COLLAPSAR_LOCK *b);
TableList *db_lock_have_duplicate(Session *session,
                                      TableList *needle   ,
                                      TableList *haystack);
bool lock_global_read_lock   (Session *session);
void unlock_global_read_lock (Session *session);
bool wait_if_global_read_lock(Session *session,
                              bool abort_on_refresh,
                              bool is_not_commit);
void start_waiting_global_read_lock    (Session *session);
bool make_global_read_lock_block_commit(Session *session);
bool set_protect_against_global_read_lock  (void);
void unset_protect_against_global_read_lock(void);
void broadcast_refresh(void);
int  try_transactional_lock  (Session *session, TableList *table_list);
int  check_transactional_lock(Session *session, TableList *table_list);
int  set_handler_table_locks (Session *session, TableList *table_list,
                              bool transactional);

/* Lock based on name */
int lock_and_wait_for_table_name(Session *session, TableList *table_list);
int lock_table_name             (Session *session, TableList *table_list,
                                 bool check_in_use);
void unlock_table_name          (Session *session, TableList *table_list);
bool wait_for_locked_table_names(Session *session, TableList *table_list);
bool lock_table_names           (Session *session, TableList *table_list);
void unlock_table_names                  (Session *session,
                                          TableList *table_list,
                                          TableList *last_table);
bool lock_table_names_exclusively(Session *session, TableList *table_list);
bool is_table_name_exclusively_locked_by_this_thread(Session *session,
                                                     TableList *table_list);
bool is_table_name_exclusively_locked_by_this_thread(Session *session,
                                                     unsigned char *key,
                                                     int key_length);


/*===========================================================================*
*                                                                            *
*                                                                            *
*                The Backend lock.                                           *
*                                                                            *
*                                                                            *
*============================================================================*/
class Chunk;
class Database;
class Transaction;
class Bucket ;

class LockInfo
{
    LockInfo (const LockInfo&);
    LockInfo& operator= (const LockInfo&);
public:
    signed int noOfReaders_;
    //-1 indicates write lock is taken

    short waitReaders_;
    short waitWriters_;

    LockInfo():noOfReaders_(0),waitReaders_(0),waitWriters_ (0) {}
    inline LockInfo& operator = (LockInfo& rhs)
    {
      this->noOfReaders_ = rhs.noOfReaders_;
      this->waitReaders_ = rhs.waitReaders_;
      this->waitWriters_ = rhs.waitWriters_;
      return *this     ;
    }
    virtual ~LockInfo(){}
};

class LockHashNode
{
public:

    void *ptrToTuple_;

    LockInfo lInfo_;

    LockHashNode *next_;

    LockHashNode():ptrToTuple_(0),
                   next_(0)
                   {}
    inline void print()
    {       }
};

class LockListIter
{
    LockHashNode *iter;
public:
    LockListIter(){}
    LockListIter(LockHashNode *head) { iter = head;}
    LockHashNode* next();
    friend class LockList;
};

class TransHashNode
{
    TransHashNode& operator =(const TransHashNode&);
public:
    LockHashNode * node_;
    TransHashNode *next_;
    void print() { node_->print(); }
};

class DatabaseManagerImpl;
//singleton
//DatabaseManager has this object and give reference to it to
//Table interface and so on.
class LockManager
{
public:

    Bucket *lockBuckets;
private:
    LockHashNode* allocLockNode(LockInfo &info,
                                void *tuple,
                                BackEnd::DbRetVal *rv);

    void  deallocLockNode(LockHashNode *head, Bucket *bucket);

    void  deallocLockNode(LockHashNode *thenode);

    Bucket* getLockBucket(void *tuple);
    Database *systemDatabase_;
    Database *userDatabase_;
    DatabaseManagerImpl* dbmgr_ ;
    LockManager() {} ;

    LockManager(const LockManager& rhs);

    LockManager& operator  = (const LockManager& rhs);
public:

    LockManager(Database *sysDb_);

    LockManager(Database *sysDb_,Database* userDb_,DatabaseManagerImpl* dbmanger);

    BackEnd::DbRetVal getSharedLock     (void * tuple, Transaction **trans);
    BackEnd::DbRetVal getExclusiveLock  (void * tuple, Transaction **trans);
    BackEnd::DbRetVal releaseLock       (void * tuple                     );
    BackEnd::DbRetVal isExclusiveLocked (void *tuple,
                                         Transaction **trans,
                                         bool &status);

    inline Database* getDatabase(){ return systemDatabase_ ;}
    void printUsageStatistics();
    void printDebugInfo();
};


#endif    //__LOCK_H__
